package com.gking.centerSystem.realm;


import com.gking.centerSystem.utils.GetAuthInfoUtil;
import com.gking.centerSystem.utils.LimitAccessUtil;
import com.gking.centerSystem.utils.commonUtil.RandomGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.gking.centerSystem.common.RedisConstants.Login_User_Auth;


@Slf4j
public class CustomAuthFilter extends AuthenticatingFilter {

    private final StringRedisTemplate stringRedisTemplate;

    public CustomAuthFilter(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }


    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        // 1.1 设置获取认证信息工具类
        HttpServletRequest req = (HttpServletRequest) request;
        GetAuthInfoUtil getAuthInfoUtil = new GetAuthInfoUtil(stringRedisTemplate, req);

        // 1.2 获取当前用户服务端信息 (登录用户userKey, 用户id, 登录ip, 登录设备信息)
        String tokenUserKey = getAuthInfoUtil.getTokenUserKey();
        String userId = getAuthInfoUtil.getUserServerInfo();
        String userLoginIp = getAuthInfoUtil.getUserLoginIp();
        String userDeviceInfo = getAuthInfoUtil.getUserDeviceInfo();

        // 1.3 获取当前客户端请求信息 (请求地址, 请求ip, 请求设备id)
        String url = req.getRequestURI();
        String ipAddress = GetAuthInfoUtil.getClientIp(req);
        String equipment = GetAuthInfoUtil.getClientDeviceInfo(req);

        // 2.1 判断用户服务端信息是否为空
        if (userId == null || userLoginIp == null || userDeviceInfo == null) {
            log.info("用户未存有登录凭证");
            return null;
        }

        // myRealmTwo认证时使用的 临时密码
        String pwd = RandomGenerator.generateRandomString(12);
        stringRedisTemplate.opsForValue().set(
                Login_User_Auth + tokenUserKey, pwd,
                2L, TimeUnit.SECONDS);

        log.info("equipment : {} ", equipment);
        log.info("userDeviceInfo : {} ", userDeviceInfo);

        // 2.2 判断用户服务端登录设备信息与本次客户端请求设备是否匹配
        if (!Objects.equals(equipment, userDeviceInfo)) {
            log.info("用户登录设备不匹配");
            return null;
        }

        // 2.2.1 判断访问的是否为特殊接口
        if (LimitAccessUtil.checkNoSameEquipment(url) && LimitAccessUtil.remoteSubAccess(ipAddress))
            return new CustomAuthToken(tokenUserKey, pwd);

        // 2.3 判断用户服务端登录IP信息与本次客户端请求IP是否匹配
        if (!Objects.equals(ipAddress, userLoginIp)) {
            log.info("用户登录IP不匹配");
            return null;
        }

        return new CustomAuthToken(tokenUserKey, pwd);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);

        if (token == null) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access token is missing");
            return false;
        }

        try {
            Subject subject = getSubject(request, response);
            subject.login(token);
            log.info("CustomAuthFilter 用户授权登录成功");
            return true;
        } catch (AuthenticationException e) {
            log.info("AuthenticationException : {}", e.getMessage());
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid access token");
            return false;
        }
    }

    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        log.info("onLoginSuccess");
        return true;
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        try {
            log.info("onLoginFailure : {}", e.getMessage());
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        return false;
    }
}
